package com.lyncs.ods.modules.company.service.impl;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.RandomUtil;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.serializer.SerializerFeature;
import com.baomidou.mybatisplus.extension.conditions.query.LambdaQueryChainWrapper;
import com.baomidou.mybatisplus.extension.conditions.update.LambdaUpdateChainWrapper;
import com.lyncs.ods.common.api.CommonPage;
import com.lyncs.ods.common.exception.ApiException;
import com.lyncs.ods.common.web.RequestHolder;
import com.lyncs.ods.constant.LyncsOdsConstant;
import com.lyncs.ods.modules.company.model.*;
import com.lyncs.ods.modules.company.service.*;
import com.lyncs.ods.modules.goods.model.SpuInfo;
import com.lyncs.ods.modules.goods.service.SpuInfoService;
import com.lyncs.ods.modules.setting.model.AppSetting;
import com.lyncs.ods.modules.setting.model.CountryInfo;
import com.lyncs.ods.modules.setting.model.PaymentAttr;
import com.lyncs.ods.modules.setting.model.PaymentSetting;
import com.lyncs.ods.modules.setting.service.AppSettingService;
import com.lyncs.ods.modules.setting.service.CountryInfoService;
import com.lyncs.ods.modules.setting.service.PaymentSettingService;
import com.lyncs.ods.modules.txn.model.DeliverInfo;
import com.lyncs.ods.modules.txn.model.OrderInfo;
import com.lyncs.ods.modules.txn.model.SettlementInfo;
import com.lyncs.ods.modules.txn.service.DeliverInfoService;
import com.lyncs.ods.modules.txn.service.OrderInfoService;
import com.lyncs.ods.modules.txn.service.SettlementInfoService;
import com.lyncs.ods.modules.user.model.CompanyUserRelation;
import com.lyncs.ods.modules.user.model.CompanyUserRelationLog;
import com.lyncs.ods.modules.user.model.UserInfo;
import com.lyncs.ods.modules.user.service.CompanyUserRelationLogService;
import com.lyncs.ods.modules.user.service.CompanyUserRelationService;
import com.lyncs.ods.modules.user.service.UserInfoService;
import com.lyncs.ods.modules.user.service.UserService;
import com.lyncs.ods.req.*;
import com.lyncs.ods.resp.*;
import com.lyncs.ods.utils.PageUtil;
import com.lyncs.ods.utils.SpuUtils;
import com.lyncs.ods.utils.TxnCommonUtils;
import lombok.extern.slf4j.Slf4j;
import me.chanjar.weixin.common.error.WxErrorException;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDateTime;
import java.util.*;
import java.util.function.BiFunction;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * @author alex
 * @date 2022/4/10 14:05
 * @description
 */
@Slf4j
@Service
@Transactional(rollbackFor = Exception.class)
public class CompanyServiceImpl implements CompanyService {
    @Autowired
    private UserInfoService userInfoService;
    @Autowired
    private UserService userService;
    @Autowired
    private CompanyInfoService companyInfoService;
    @Autowired
    private CompanyUserRelationService companyUserRelationService;
    @Autowired
    private CompanyUserRelationLogService relationRecordService;
    @Autowired
    private CompanyContactInfoService companyContactInfoService;
    @Autowired
    private CompanyFeeInfoService companyFeeInfoService;
    @Autowired
    private CompanyPaymentInfoService companyPaymentInfoService;
    @Autowired
    private PaymentSettingService paymentSettingService;
    @Autowired
    private CompanySpuRelationService companySpuRelationService;
    @Autowired
    private CountryInfoService countryInfoService;
    @Autowired
    private SpuInfoService spuInfoService;
    @Autowired
    private OrderInfoService orderInfoService;
    @Autowired
    private DeliverInfoService deliverInfoService;
    @Autowired
    private SettlementInfoService settlementInfoService;
    @Autowired
    private AppSettingService appSettingService;
    @Autowired
    private CompanyRoleGroupService companyRoleGroupService;
    @Autowired
    private CompanyRelationRoleService companyRelationRoleService;
    @Autowired
    private CompanyUserRoleService companyUserRoleService;


    /**
     * 用户注册公司并关联
     *
     * @param req req
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public UserCompanyResp registerCompany(SaveCompanyReq req) {
        CompanyInfo companyInfo = new CompanyInfo();
        BeanUtils.copyProperties(req, companyInfo);
        companyInfo.setId(IdUtil.getSnowflakeNextId()).setCreator(RequestHolder.getUserId()).setCreateTime(RequestHolder.getRequestDateTime()).setUpdateTime(RequestHolder.getRequestDateTime());
        companyInfoService.save(companyInfo);
        companyUserRelation(RequestHolder.getUserId(), companyInfo.getId(), LyncsOdsConstant.CompanyRole.ADMIN.getKey());
        userInfoService.lambdaUpdate().eq(UserInfo::getId, RequestHolder.getUserId())
                .set(UserInfo::getRecentLoginCompanyId, companyInfo.getId())
                .update();

        saveOtherInfo(companyInfo.getId());
        RequestHolder.saveCookie(LyncsOdsConstant.Header.COOKIE_PC, companyInfo.getId().toString());
        CountryInfo countryInfo = countryInfoService.lambdaQuery().eq(CountryInfo::getId, companyInfo.getCountry()).one();
        UserCompanyResp resp = new UserCompanyResp();
        resp.setCompanyId(companyInfo.getId());
        resp.setShortName(companyInfo.getShortName());
        resp.setAvatarUrl(companyInfo.getAvatarUrl());
        resp.setUserId(RequestHolder.getUserId());
        resp.setCountryName(countryInfo == null ? null : countryInfo.getName());
        resp.setCurrency(countryInfo == null ? null : countryInfo.getCurrency());
        return resp;
    }

    /**
     * 保存企业全局配置项
     *
     * @param companyId companyId
     */
    private void saveOtherInfo(Long companyId) {
        //插入默认的款项信息
        List<CompanyFeeInfo> commonFeeInfos = getCommonFeeInfos();
        if (CollectionUtil.isNotEmpty(commonFeeInfos)) {
            List<CompanyFeeInfo> companyFeeInfos = commonFeeInfos.stream().map(fee -> {
                CompanyFeeInfo feeInfo = new CompanyFeeInfo();
                BeanUtils.copyProperties(fee, feeInfo);
                feeInfo.setCompanyId(companyId)
                        .setId(IdUtil.getSnowflakeNextId())
                        .setCreateTime(RequestHolder.getRequestDateTime())
                        .setUpdateTime(RequestHolder.getRequestDateTime())
                        .setStatus(1);
                return feeInfo;
            }).collect(Collectors.toList());
            log.info("save company fees, companyFeeInfos:{}", JSONObject.toJSONString(companyFeeInfos));
            companyFeeInfoService.saveBatch(companyFeeInfos);
        }
        List<PaymentSetting> paymentSettings = paymentSettingService.lambdaQuery().eq(PaymentSetting::getStatus, 1).list();
        if (CollectionUtils.isNotEmpty(paymentSettings)) {
            List<CompanyPaymentInfo> settings = paymentSettings.stream().map(i -> {
                String paymentAttrs = i.getAttrJson();
                JSONObject paymentAttrJson = JSONObject.parseObject(paymentAttrs);
                List<PaymentAttr> paymentAttrList = paymentAttrJson.entrySet().stream().map(attr -> new PaymentAttr().setKey(attr.getKey()).setLabel(attr.getValue().toString()).setValue(null)).collect(Collectors.toList());
                return new CompanyPaymentInfo()
                        .setCompanyId(companyId)
                        .setPaymentId(i.getId())
                        .setStatus(LyncsOdsConstant.EnableStatus.DISABLE.getKey())
                        .setCreateTime(RequestHolder.getRequestDateTime())
                        .setUpdateTime(RequestHolder.getRequestDateTime())
                        .setPaymentSetting(JSONArray.toJSONString(paymentAttrList, SerializerFeature.WriteMapNullValue));
            }).collect(Collectors.toList());
            log.info("save company fees, companyPaymentInfos:{}", JSONObject.toJSONString(settings));
            companyPaymentInfoService.saveBatch(settings);
        }
    }

    /**
     * create one company for Partner
     *
     * @param req request info
     * @return cid
     */
    @Override
    public Long savePartnerCompany(SavePartnerCompanyReq req) {
        Long companyId = req.getCompanyId();
        LocalDateTime requestTime = RequestHolder.getRequestDateTime();
        //支持的所有关系类型
        List<Integer> rawTypes = List.of(LyncsOdsConstant.ContactType.SUPPLIER.getKey(), LyncsOdsConstant.ContactType.CUSTOMER.getKey());
        List<Integer> reqTypes = Stream.of(req.getType().split(LyncsOdsConstant.Common.COMMA)).filter(Objects::nonNull).map(Integer::valueOf).filter(rawTypes::contains).collect(Collectors.toList());
        if (CollectionUtils.isEmpty(reqTypes)) {
            log.warn("wrong type:{}", req.getType());
            throw new ApiException("合作企业关系错误");
        }
        Boolean hasGroup = CollectionUtils.isNotEmpty(req.getGroupIds());
        List<CompanyContactInfo> insertContactInfos = new ArrayList<>();
        List<CompanyContactInfo> updateContactInfos = new ArrayList<>();
        List<Long> delContactIds = new ArrayList<>();
        if (companyId != null) {
            //update
            List<CompanyContactInfo> companyContactInfos = companyContactInfoService.lambdaQuery()
                    .eq(CompanyContactInfo::getRelationId, req.getCompanyId())
                    .eq(CompanyContactInfo::getCompanyId, RequestHolder.getCompanyId())
                    .eq(CompanyContactInfo::getStatus, LyncsOdsConstant.BindStatus.BIND.getKey())
                    .list();
            if (CollectionUtils.isEmpty(companyContactInfos)) {
                throw new ApiException("不是您的合作企业，无法编辑");
            }
            companyContactInfos.forEach(info -> {
                info.setNickname(req.getNickname());
                ContactInfo contactInfo = new ContactInfo();
                BeanUtils.copyProperties(req, contactInfo);
                info.setContactInfo(JSONObject.toJSONString(contactInfo, SerializerFeature.WriteMapNullValue));
            });
            CompanyContactInfo snapshotInfo = companyContactInfos.get(0);
            Map<Integer, List<CompanyContactInfo>> existTypeMap = companyContactInfos.stream().collect(Collectors.groupingBy(CompanyContactInfo::getType));
            Set<Integer> existTypes = existTypeMap.keySet();
            Set<Integer> allTypes = new HashSet<>(reqTypes);
            allTypes.addAll(existTypes);
            for (Integer t : allTypes) {
                if (existTypes.contains(t) && reqTypes.contains(t)) {
                    updateContactInfos.addAll(existTypeMap.get(t));
                } else if (existTypes.contains(t)) {
                    delContactIds.addAll(existTypeMap.get(t).stream().map(CompanyContactInfo::getId).collect(Collectors.toList()));
                } else {
                    CompanyContactInfo newContactInfo = new CompanyContactInfo();
                    BeanUtils.copyProperties(snapshotInfo, newContactInfo);
                    newContactInfo.setId(IdUtil.getSnowflakeNextId())
                            .setCreateTime(requestTime)
                            .setType(t)
                            .setUpdateTime(requestTime);
                    insertContactInfos.add(newContactInfo);
                }
            }
        } else {
            //获取配置项
            List<AppSetting> settings = appSettingService.lambdaQuery().in(AppSetting::getSettingKey, List.of("virtualCompanyAvatar", "virtualCompanyBackground"))
                    .eq(AppSetting::getStatus, LyncsOdsConstant.EnableStatus.ENABLE.getKey())
                    .list();
            AppSetting virtualCompanyAvatar = settings.stream().filter(i -> "virtualCompanyAvatar".equals(i.getSettingKey())).findFirst().orElse(null);
            AppSetting virtualCompanyBackground = settings.stream().filter(i -> "virtualCompanyBackground".equals(i.getSettingKey())).findFirst().orElse(null);
            Function<JSONArray, String> randomFunc = (arr) -> arr == null ? null : arr.getString(RandomUtil.randomInt(0, arr.size()));
            Function<AppSetting, JSONArray> settingFunc = (app) -> (app != null && StringUtils.isNotBlank(app.getSettingValue())) ? JSONArray.parseArray(app.getSettingValue()) : null;

            companyId = IdUtil.getSnowflakeNextId();
            CompanyInfo virtualCompany = new CompanyInfo()
                    .setId(companyId)
                    .setStatus(LyncsOdsConstant.CompanyStatus.VIRTUAL.getKey())
                    .setAvatarUrl(settingFunc.andThen(randomFunc).apply(virtualCompanyAvatar))
                    .setBackgroundImg(settingFunc.andThen(randomFunc).apply(virtualCompanyBackground))
                    .setCreator(RequestHolder.getUserId())
                    .setCreateTime(RequestHolder.getRequestDateTime())
                    .setUpdateTime(RequestHolder.getRequestDateTime())
                    .setShortName(req.getNickname())
                    .setCountry(req.getCountry());
            companyInfoService.save(virtualCompany);

            CompanyInfo currentCompanyInfo = companyInfoService.getAvailableCompanyInfo(RequestHolder.getCompanyId());
            for (Integer t : reqTypes) {
                if (!rawTypes.contains(t)) {
                    throw new ApiException("非法关系类型");
                }
                Integer oppositeType = rawTypes.stream().filter(k -> !k.equals(t)).findFirst().orElse(null);
                //正向
                CompanyContactInfo positiveContactInfo = new CompanyContactInfo()
                        .setId(IdUtil.getSnowflakeNextId())
                        .setCompanyId(RequestHolder.getCompanyId())
                        .setRelationId(virtualCompany.getId())
                        .setInitCompanyId(virtualCompany.getId())
                        .setNickname(req.getNickname())
                        .setStatus(LyncsOdsConstant.BindStatus.BIND.getKey())
                        .setCreateTime(RequestHolder.getRequestDateTime())
                        .setType(t);
                ContactInfo contactInfo = new ContactInfo();
                BeanUtils.copyProperties(req, contactInfo);
                positiveContactInfo.setContactInfo(JSONObject.toJSONString(contactInfo, SerializerFeature.WriteMapNullValue));

                //逆向关联
                CompanyContactInfo oppositeContactInfo = new CompanyContactInfo()
                        .setId(IdUtil.getSnowflakeNextId())
                        .setCompanyId(virtualCompany.getId())
                        .setInitCompanyId(null)
                        .setRelationId(currentCompanyInfo.getId())
                        .setNickname(currentCompanyInfo.getShortName())
                        .setStatus(LyncsOdsConstant.BindStatus.BIND.getKey())
                        .setCreateTime(RequestHolder.getRequestDateTime())
                        .setType(oppositeType);

                insertContactInfos.add(positiveContactInfo);
                insertContactInfos.add(oppositeContactInfo);
            }
        }
        log.info("update company contact infos,insert:{},update:{},delete:{}", insertContactInfos, updateContactInfos, delContactIds);
        if (CollectionUtils.isNotEmpty(insertContactInfos)) {
            companyContactInfoService.saveBatch(insertContactInfos);
        }
        if (CollectionUtils.isNotEmpty(delContactIds)) {
            companyContactInfoService.lambdaUpdate()
                    .set(CompanyContactInfo::getStatus, LyncsOdsConstant.BindStatus.UNBIND.getKey())
                    .in(CompanyContactInfo::getId, delContactIds)
                    .update();
        }
        if (CollectionUtils.isNotEmpty(updateContactInfos)) {
            companyContactInfoService.updateBatchById(updateContactInfos);
        }
        //更新权限相关
        companyRelationRoleService.saveGroups(RequestHolder.getCompanyId(), companyId, req.getGroupIds());
        companyContactInfoService.lambdaUpdate()
                .eq(CompanyContactInfo::getCompanyId, RequestHolder.getCompanyId())
                .eq(CompanyContactInfo::getRelationId, companyId)
                .set(CompanyContactInfo::getHasRoleGroup, hasGroup)
                .update();
        return companyId;
    }

    @Override
    public CommonPage<PartnerInfoResp> listPartnerCompanies(Integer type, Integer page, Integer pageSize, Long relationId) {
        Long companyId = RequestHolder.getCompanyId();
        Long userId = RequestHolder.getUserId();
        Boolean isAdmin = RequestHolder.isAdmin();
        List<Long> partnerIds = companyUserRoleService.listAllAvailablePartnerId(userId, companyId);
        log.info("starting query partner companies, condition: companyId -> {}, userId -> {}, isAdmin -> {}, relationIds(visible to the user) -> {},", companyId, userId, isAdmin, partnerIds);
        LambdaQueryChainWrapper<CompanyContactInfo> wrapper = companyContactInfoService.lambdaQuery()
                .eq(CompanyContactInfo::getStatus, LyncsOdsConstant.BindStatus.BIND.getKey())
                .eq(CompanyContactInfo::getCompanyId, RequestHolder.getCompanyId())
                .eq(CompanyContactInfo::getType, type);
        if (!isAdmin) {
            //非管理员只能查看没有加入权限组或在指定权限组内的合作企业
            wrapper.and(w -> w.eq(CompanyContactInfo::getHasRoleGroup, 0).or().in(CollectionUtils.isNotEmpty(partnerIds), CompanyContactInfo::getRelationId, partnerIds));

        }
        CommonPage<PartnerInfoResp> commonPage = PageUtil.count(wrapper, page, pageSize);
        if (commonPage.getTotal() == 0) {
            return commonPage;
        }
        List<CompanyContactInfo> companyContactInfos = wrapper.last(PageUtil.getLastSql(page, pageSize, "relation_id", relationId)).list();
        List<Long> allCompanyIds = companyContactInfos.stream().map(CompanyContactInfo::getRelationId).collect(Collectors.toList());
        List<CompanyInfo> companyInfos = companyInfoService.getAvailableCompanyInfos(allCompanyIds);
        Map<Long, CompanyInfo> companyInfoMap = companyInfos.stream().collect(Collectors.toMap(CompanyInfo::getId, o -> o, (o1, o2) -> o2));
        Function<CompanyContactInfo, PartnerInfoResp> covertFunc = (info) -> {
            CompanyInfo companyInfo = companyInfoMap.get(info.getRelationId());
            if (Objects.isNull(companyInfo)) {
                return null;
            }
            ContactInfo contactInfo = info.getContactDetail();
            PartnerInfoResp partnerInfoResp = new PartnerInfoResp();
            partnerInfoResp.setAvatarUrl(companyInfo.getAvatarUrl())
                    .setCompanyId(companyInfo.getId())
                    .setShortName(info.getNickname())
                    .setName(companyInfo.getName())
                    .setRegister(!info.getRelationId().equals(info.getInitCompanyId()))
                    .setAddress(contactInfo.getAddress())
                    .setRelated(Boolean.TRUE);
            partnerInfoResp.setType(info.getType());
            return partnerInfoResp;
        };
        return PageUtil.result(companyContactInfos, covertFunc, commonPage);
    }

    /**
     * get staff info by company id
     *
     * @param companyId companyId
     * @return staffs
     */
    @Override
    public List<StaffInfoResp> listCompanyStaffs(Long companyId) {
        List<CompanyUserRelation> companyUserRelations = companyUserRelationService.lambdaQuery().eq(CompanyUserRelation::getCompanyId, companyId).eq(CompanyUserRelation::getStatus, LyncsOdsConstant.BindStatus.BIND.getKey()).list();
        if (CollectionUtil.isEmpty(companyUserRelations)) {
            return null;
        }
        Map<Long, CompanyUserRelation> userRelationMap = companyUserRelations.stream().collect(Collectors.toMap(CompanyUserRelation::getUserId, o -> o, (o1, o2) -> o1));
        List<UserInfo> userInfos = userInfoService.listByIds(userRelationMap.keySet());
        List<Long> userIds = userInfos.stream().map(UserInfo::getId).collect(Collectors.toList());
        //Map<Long, List<UserGroupInfo>> userGroupInfoMap = companyUserRoleService.getUserGroupInfos(companyId, userIds);
        Map<Long, List<Long>> userGroupMap = companyUserRoleService.getUserGroupRoleIds(companyId, userIds);
        return userInfos.stream().map(info -> {
            StaffInfoResp resp = new StaffInfoResp();
            CompanyUserRelation companyUserInfo = userRelationMap.get(info.getId());
            BeanUtils.copyProperties(info, resp);
            resp.setName(info.getUsername())
                    .setUserId(companyUserInfo.getUserId())
                    .setNickname(companyUserInfo.getNickname())
                    .setDept(companyUserInfo.getDept())
                    .setPost(companyUserInfo.getPost())
                    .setRole(companyUserInfo.getRole())
                    .setRemark(companyUserInfo.getRemark())
                    .setGroupIds(userGroupMap.get(info.getId()));
            return resp;
        }).collect(Collectors.toList());
    }

    @Override
    public List<PaymentSettingResp> listPaymentInfo(Long companyId) {
        List<CompanyPaymentInfo> companyPaymentInfos = companyPaymentInfoService.lambdaQuery().eq(CompanyPaymentInfo::getCompanyId, companyId)
                .list();
        List<PaymentSetting> paymentSettings = paymentSettingService.lambdaQuery().eq(PaymentSetting::getStatus, LyncsOdsConstant.EnableStatus.ENABLE.getKey()).list();
        Map<Long, PaymentSetting> paymentSettingMap = paymentSettings.stream().collect(Collectors.toMap(PaymentSetting::getId, o -> o, (o1, o2) -> o2));

        return companyPaymentInfos.stream().map(info -> {
            PaymentSetting paymentSetting = paymentSettingMap.get(info.getPaymentId());
            if (Objects.isNull(paymentSetting)) {
                return null;
            }
            return new PaymentSettingResp()
                    .setId(info.getId())
                    .setPaymentId(info.getPaymentId())
                    .setCode(paymentSetting.getCode())
                    .setName(paymentSetting.getName())
                    .setLogo(paymentSetting.getLogo())
                    .setCompanyId(companyId)
                    .setStatus(info.getStatus())
                    .setAttrInfos(JSONArray.parseArray(info.getPaymentSetting(), PaymentAttr.class));
        }).filter(Objects::nonNull).collect(Collectors.toList());
    }

    @Override
    public void savePaymentInfo(CompanyPaymentEditReq req) {
        CompanyPaymentInfo paymentInfo = companyPaymentInfoService.lambdaQuery().eq(CompanyPaymentInfo::getId, req.getId()).one();
        if (paymentInfo == null) {
            throw new ApiException("company payment setting not found, id:" + req.getId());
        }
        String setting = paymentInfo.getPaymentSetting();
        List<PaymentAttr> attrs = JSONArray.parseArray(setting, PaymentAttr.class);
        List<String> reqAttrKey = req.getAttrInfos().stream().map(PaymentAttr::getKey).collect(Collectors.toList());
        List<String> existAttrKey = attrs.stream().map(PaymentAttr::getKey).collect(Collectors.toList());

        //属性校验
        Collection<String> subtractAttrKey = CollectionUtils.subtract(reqAttrKey, existAttrKey);
        if (CollectionUtils.isNotEmpty(subtractAttrKey)) {
            String subtractAttrKeyStr = String.join(LyncsOdsConstant.Common.COMMA, subtractAttrKey);
            throw new ApiException("unknown keys:" + subtractAttrKeyStr);
        }
        companyPaymentInfoService.lambdaUpdate().set(CompanyPaymentInfo::getStatus, req.getStatus())
                .set(CompanyPaymentInfo::getPaymentSetting, JSONArray.toJSONString(req.getAttrInfos(), SerializerFeature.WriteMapNullValue))
                .eq(CompanyPaymentInfo::getId, req.getId())
                .update();
    }

    @Override
    public List<String> listCompanyFees(Long companyId) {
        List<CompanyFeeInfo> companyFeeInfos = companyFeeInfoService.lambdaQuery().eq(CompanyFeeInfo::getCompanyId, companyId).eq(CompanyFeeInfo::getStatus, LyncsOdsConstant.EnableStatus.ENABLE.getKey()).list();
        return companyFeeInfos.stream().map(CompanyFeeInfo::getFeeName).collect(Collectors.toList());
    }

    @Override
    public void saveCompanyFees(List<String> feeNames) {
        List<CompanyFeeInfo> companyFeeInfos = feeNames.stream().map(name -> new CompanyFeeInfo()
                .setId(IdUtil.getSnowflakeNextId())
                .setCompanyId(RequestHolder.getCompanyId())
                .setFeeName(name)
                .setUpdateTime(RequestHolder.getRequestDateTime())
                .setCreateTime(RequestHolder.getRequestDateTime())
                .setStatus(LyncsOdsConstant.EnableStatus.ENABLE.getKey())).collect(Collectors.toList());
        companyFeeInfoService.lambdaUpdate().eq(CompanyFeeInfo::getCompanyId, RequestHolder.getCompanyId()).remove();
        companyFeeInfoService.saveBatch(companyFeeInfos);
    }

    @Override
    public void updatePaymentStatus(Long id, Integer status) {
        CompanyPaymentInfo companyPaymentInfo = companyPaymentInfoService.lambdaQuery().eq(CompanyPaymentInfo::getId, id).one();
        if (Objects.isNull(companyPaymentInfo) || companyPaymentInfo.getStatus().equals(status)) {
            throw new ApiException("payment info does not exist or already in the current status");
        }
        companyPaymentInfoService.lambdaUpdate().eq(CompanyPaymentInfo::getId, id).set(CompanyPaymentInfo::getStatus, status).update();
    }

    /**
     * get company detail info by id
     *
     * @param companyId companyId
     * @return detail info
     */
    @Override
    public CompanyDetailInfoResp getCompanyDetailInfo(Long companyId, String type) {
        Long userId = null;
        try {
            userId = RequestHolder.getUserId();
        } catch (Exception ignored) {

        }
        CompanyInfo companyInfo = companyInfoService.getAvailableCompanyInfo(companyId);
        if (Objects.isNull(companyInfo)) {
            throw new ApiException("查看企业不存在或已删除");
        }
        CompanyDetailInfoResp companyDetailInfoResp = new CompanyDetailInfoResp(companyInfo);
        //非合作企业查看是否为管理员
        if (!"partner".equals(type) && userId != null) {
            Boolean isAdmin = companyUserRelationService.lambdaQuery().eq(CompanyUserRelation::getCompanyId, companyId).eq(CompanyUserRelation::getUserId, userId).eq(CompanyUserRelation::getRole, LyncsOdsConstant.CompanyRole.ADMIN.getKey()).count() > 0;
            companyDetailInfoResp.setCanEdit(isAdmin);
        }
        if ("partner".equals(type)) {
            //查看合作企业详情
            Long thisCompanyId = RequestHolder.getCompanyId();
            List<CompanyContactInfo> contactInfos = companyContactInfoService.lambdaQuery()
                    .eq(CompanyContactInfo::getCompanyId, thisCompanyId)
                    .and(w -> w.eq(CompanyContactInfo::getRelationId, companyId).or().eq(CompanyContactInfo::getInitCompanyId, companyId))
                    .eq(CompanyContactInfo::getStatus, LyncsOdsConstant.BindStatus.BIND.getKey())
                    .list();
            if (CollectionUtils.isEmpty(contactInfos)) {
                throw new ApiException("不是您的合作企业，无法查看");
            }
            Map<Long, List<CompanyGroupInfo>> groupMap = companyRelationRoleService.getRelationGroup(thisCompanyId, List.of(companyId));
            CompanyContactInfo companyContactInfo = contactInfos.get(0);
            companyDetailInfoResp = new CompanyDetailInfoResp(companyInfo, companyContactInfo);
            companyDetailInfoResp.setType(contactInfos.stream().map(CompanyContactInfo::getType).filter(Objects::nonNull).map(String::valueOf).distinct().collect(Collectors.joining(LyncsOdsConstant.Common.COMMA)));
            //交易信息
            ListPageSearchReq sellerReq = new ListPageSearchReq(1, companyId);
            ListPageSearchReq buyerReq = new ListPageSearchReq(2, companyId);
            Integer orderSellerCount = TxnCommonUtils.buildLambdaQueryWrapper(orderInfoService.lambdaQuery().setEntityClass(OrderInfo.class).ne(OrderInfo::getStatus, LyncsOdsConstant.OrderStatus.DELETED.getKey()), sellerReq, null).count();
            Integer orderBuyerCount = TxnCommonUtils.buildLambdaQueryWrapper(orderInfoService.lambdaQuery().setEntityClass(OrderInfo.class).ne(OrderInfo::getStatus, LyncsOdsConstant.OrderStatus.DELETED.getKey()), buyerReq, null).count();
            Integer deliverSellerCount = TxnCommonUtils.buildLambdaQueryWrapper(deliverInfoService.lambdaQuery().setEntityClass(DeliverInfo.class).ne(DeliverInfo::getStatus, LyncsOdsConstant.DeliverStatus.DELETED.getKey()), sellerReq, null).count();
            Integer deliverBuyerCount = TxnCommonUtils.buildLambdaQueryWrapper(deliverInfoService.lambdaQuery().setEntityClass(DeliverInfo.class).ne(DeliverInfo::getStatus, LyncsOdsConstant.DeliverStatus.DELETED.getKey()), buyerReq, null).count();
            Integer settlementSellerCount = TxnCommonUtils.buildLambdaQueryWrapper(settlementInfoService.lambdaQuery().setEntityClass(SettlementInfo.class).ne(SettlementInfo::getStatus, LyncsOdsConstant.SettlementStatus.DELETED.getKey()), sellerReq, null).count();
            Integer settlementBuyerCount = TxnCommonUtils.buildLambdaQueryWrapper(settlementInfoService.lambdaQuery().setEntityClass(SettlementInfo.class).ne(SettlementInfo::getStatus, LyncsOdsConstant.SettlementStatus.DELETED.getKey()), buyerReq, null).count();
            companyDetailInfoResp.setOrderInfo(new CompanyDetailInfoResp.CountInfo(orderSellerCount, orderBuyerCount));
            companyDetailInfoResp.setDeliverInfo(new CompanyDetailInfoResp.CountInfo(deliverSellerCount, deliverBuyerCount));
            companyDetailInfoResp.setSettlementInfo(new CompanyDetailInfoResp.CountInfo(settlementSellerCount, settlementBuyerCount));
            companyDetailInfoResp.setCompanyGroupInfos(groupMap.get(companyId));
        }
        //设置国家&&货币信息
        setCountryInfo(companyDetailInfoResp);
        return companyDetailInfoResp;
    }

    private void setCountryInfo(CompanyDetailInfoResp companyDetailInfoResp) {
        if (companyDetailInfoResp.getCountryId() == null) {
            return;
        }
        CountryInfo countryInfo = countryInfoService.lambdaQuery().eq(CountryInfo::getId, companyDetailInfoResp.getCountryId()).one();
        if (Objects.nonNull(countryInfo)) {
            companyDetailInfoResp.setCountryName(countryInfo.getName());
            companyDetailInfoResp.setCurrency(countryInfo.getCurrency());
        }
    }

    @Override
    public CommonPage<SpuInfoResp> listCompanySpu(String companyId, Integer page, Integer pageSize) {
        LambdaQueryChainWrapper<CompanySpuRelation> wrapper = companySpuRelationService.lambdaQuery()
                .eq(CompanySpuRelation::getCompanyId, companyId)
                .eq(CompanySpuRelation::getStatus, LyncsOdsConstant.BindStatus.BIND.getKey());
        CommonPage<SpuInfoResp> commonPage = PageUtil.count(wrapper, page, pageSize);
        if (commonPage.getTotal() == 0) {
            return commonPage;
        }
        List<CompanySpuRelation> companySpuRelations = wrapper.last(PageUtil.getLastSql(page, pageSize)).list();
        List<Long> companySpuIds = companySpuRelations.stream().map(CompanySpuRelation::getSpuId).distinct().collect(Collectors.toList());
        List<SpuInfo> spuInfos = spuInfoService.lambdaQuery().in(SpuInfo::getId, companySpuIds).eq(SpuInfo::getStatus, LyncsOdsConstant.EnableStatus.ENABLE.getKey()).list();
        Function<SpuInfo, SpuInfoResp> func = (info) -> new SpuInfoResp().setSpuId(info.getId()).setName(SpuUtils.getName(info));
        return PageUtil.result(spuInfos, func, commonPage);
    }

    @Override
    public void saveCompanySpu(Long companyId, Long spuId, Integer type) {
        if (LyncsOdsConstant.BindStatus.BIND.getKey().equals(type)) {
            companySpuRelationService.save(new CompanySpuRelation()
                    .setId(IdUtil.getSnowflakeNextId())
                    .setCompanyId(companyId).setSpuId(spuId)
                    .setCreateTime(RequestHolder.getRequestDateTime())
                    .setStatus(type)
                    .setUpdateTime(RequestHolder.getRequestDateTime()));
        } else {
            companySpuRelationService.lambdaUpdate()
                    .eq(CompanySpuRelation::getCompanyId, companyId)
                    .eq(CompanySpuRelation::getSpuId, spuId)
                    .set(CompanySpuRelation::getStatus, LyncsOdsConstant.BindStatus.UNBIND.getKey())
                    .update();
        }
    }

    @Override
    public void deleteStaff(Long companyId, Long userId) {
        companyUserRelationService.lambdaUpdate()
                .eq(CompanyUserRelation::getCompanyId, companyId)
                .eq(CompanyUserRelation::getUserId, userId)
                .set(CompanyUserRelation::getStatus, LyncsOdsConstant.EnableStatus.DISABLE.getKey())
                .update();
    }

    @Override
    public void updateRole(Long companyId, Long userId, Integer role) {
        if (role != 1) {
            throw new ApiException("invalid param role");
        }
        //移除旧的管理员身份
        companyUserRelationService.lambdaUpdate()
                .eq(CompanyUserRelation::getCompanyId, companyId)
                .eq(CompanyUserRelation::getRole, role)
                .set(CompanyUserRelation::getRole, 0)
                .update();
        //设置新的管理员
        companyUserRelationService.lambdaUpdate()
                .eq(CompanyUserRelation::getCompanyId, companyId)
                .eq(CompanyUserRelation::getUserId, userId)
                .set(CompanyUserRelation::getRole, role)
                .update();
    }

    @Override
    public void releaseContact(Long companyId, Integer type) {
        List<CompanyContactInfo> companyContactInfos = companyContactInfoService.lambdaQuery()
                .eq(CompanyContactInfo::getCompanyId, RequestHolder.getCompanyId())
                .eq(CompanyContactInfo::getRelationId, companyId)
                .eq(CompanyContactInfo::getStatus, LyncsOdsConstant.BindStatus.BIND.getKey())
                .list();
        if (Objects.isNull(companyContactInfos)) {
            return;
        }
        LambdaUpdateChainWrapper<CompanyContactInfo> updateWrapper = companyContactInfoService.lambdaUpdate()
                .eq(CompanyContactInfo::getCompanyId, RequestHolder.getCompanyId())
                .eq(CompanyContactInfo::getRelationId, companyId)
                .eq(CompanyContactInfo::getStatus, LyncsOdsConstant.BindStatus.BIND.getKey());

        CompanyContactInfo first = companyContactInfos.get(0);
        //B类转A类的企业
        if (Objects.nonNull(first.getInitCompanyId()) && type == 1) {
            //从A类企业还原B类
            updateWrapper.set(CompanyContactInfo::getRelationId, first.getInitCompanyId());
            TxnCommonUtils.handlerCompanyRelationChange(RequestHolder.getCompanyId(),
                    first.getRelationId(),
                    first.getInitCompanyId(),
                    Boolean.TRUE,
                    orderInfoService.lambdaUpdate(),
                    orderInfoService.lambdaUpdate(),
                    deliverInfoService.lambdaUpdate(),
                    deliverInfoService.lambdaUpdate(),
                    settlementInfoService.lambdaUpdate(),
                    settlementInfoService.lambdaUpdate());
            companyRelationRoleService.lambdaUpdate()
                    .eq(CompanyRelationRole::getCompanyId, RequestHolder.getCompanyId())
                    .eq(CompanyRelationRole::getRelationId, first.getRelationId())
                    .set(CompanyRelationRole::getRelationId, first.getInitCompanyId())
                    .update();
        } else {
            updateWrapper.set(CompanyContactInfo::getStatus, LyncsOdsConstant.BindStatus.UNBIND.getKey());
        }
        updateWrapper.update();
    }

    @Override
    public void updateCompany(Long companyId, SaveCompanyReq req) {
        CompanyInfo companyInfo = companyInfoService.getAvailableCompanyInfo(companyId);
        if (companyInfo == null) {
            throw new ApiException("invalid company id");
        }
        BeanUtils.copyProperties(req, companyInfo);
        companyInfoService.updateById(companyInfo);
    }

    @Override
    public void saveStaffBatch(Long companyId, SaveStaffReq req) {
        Map<Long, SaveStaffReq.StaffInfo> staffInfoMap = req.getStaffInfos().stream().collect(Collectors.toMap(SaveStaffReq.StaffInfo::getUserId, o -> o));
        List<CompanyUserRelation> staffs = companyUserRelationService.lambdaQuery().eq(CompanyUserRelation::getCompanyId, companyId)
                .in(CompanyUserRelation::getUserId, staffInfoMap.keySet())
                .eq(CompanyUserRelation::getStatus, LyncsOdsConstant.BindStatus.BIND.getKey())
                .list();
        if (CollectionUtils.isEmpty(staffs)) {
            return;
        }
        staffs.forEach(s -> {
            SaveStaffReq.StaffInfo staffInfo = staffInfoMap.get(s.getUserId());
            s.setPost(staffInfo.getPost());
            s.setNickname(staffInfo.getNickname());
            s.setRemark(staffInfo.getRemark());
        });
        companyUserRelationService.updateBatchById(staffs);
    }

    @Override
    public void relateContact(PartnerRelationReq req) {
        if (req.getRelationId().equals(req.getCompanyId())) {
            throw new ApiException("不能与自己建立好友关系");
        }
        if (req.getRelationId().equals(req.getInitCompanyId())) {
            throw new ApiException("参数错误，relationId == initCompanyId");
        }
        Boolean virtualRelated = Boolean.FALSE;
        //场景较多，暂无好办法一次查询校验所有结果
        if (req.getInitCompanyId() != null) {
            //校验 initCompanyId 有没有被其它企业关联
            Integer relatedCount = companyContactInfoService.lambdaQuery()
                    .eq(CompanyContactInfo::getCompanyId, req.getCompanyId())
                    .eq(CompanyContactInfo::getStatus, LyncsOdsConstant.BindStatus.BIND.getKey())
                    .eq(CompanyContactInfo::getInitCompanyId, req.getInitCompanyId())
                    .ne(CompanyContactInfo::getRelationId, req.getRelationId())
                    .ne(CompanyContactInfo::getRelationId, req.getInitCompanyId())
                    .count();
            //B类CID是否已被占用
            if (relatedCount > 0) {
                throw new ApiException("该邀请已被其它企业使用，如有问题请联系邀请方解除关联");
            }
            virtualRelated = Boolean.TRUE;
        }
        Integer relatedCount = companyContactInfoService.lambdaQuery()
                .eq(CompanyContactInfo::getCompanyId, req.getRelationId())
                .eq(CompanyContactInfo::getStatus, LyncsOdsConstant.BindStatus.BIND.getKey())
                .eq(CompanyContactInfo::getRelationId, req.getCompanyId()).count();
        //两个A类CID是否已有好友关系
        if (relatedCount > 0) {
            throw new ApiException("好友关系已存在，请勿重复建立关联关系");
        }
        List<CompanyContactInfo> insertList = new ArrayList<>();
        //发出邀请方企业信息
        CompanyInfo companyInfo = companyInfoService.getValidCompanyInfo(req.getCompanyId());
        //接收邀请方企业信息
        CompanyInfo relatedCompanyInfo = companyInfoService.getValidCompanyInfo(req.getRelationId());
        if (req.getInitCompanyId() != null && virtualRelated) {
            //B类转A类关联，正向更新A类企业
            companyContactInfoService.lambdaUpdate()
                    .eq(CompanyContactInfo::getCompanyId, req.getCompanyId())
                    .eq(CompanyContactInfo::getRelationId, req.getInitCompanyId())
                    .eq(CompanyContactInfo::getStatus, LyncsOdsConstant.BindStatus.BIND.getKey())
                    .set(CompanyContactInfo::getRelationId, req.getRelationId())
                    .update();
            //B类转A类关联，反向关联A类企业
            for (String t : req.getType().split(LyncsOdsConstant.Common.COMMA)) {
                insertList.add(buildContactInfo(relatedCompanyInfo, companyInfo, Integer.valueOf(t)));
            }
            TxnCommonUtils.handlerCompanyRelationChange(req.getCompanyId(),
                    req.getRelationId(),
                    req.getInitCompanyId(),
                    Boolean.FALSE,
                    orderInfoService.lambdaUpdate(),
                    orderInfoService.lambdaUpdate(),
                    deliverInfoService.lambdaUpdate(),
                    deliverInfoService.lambdaUpdate(),
                    settlementInfoService.lambdaUpdate(),
                    settlementInfoService.lambdaUpdate());
            companyRelationRoleService.lambdaUpdate()
                    .eq(CompanyRelationRole::getCompanyId, req.getCompanyId())
                    .eq(CompanyRelationRole::getRelationId, req.getInitCompanyId())
                    .set(CompanyRelationRole::getRelationId, req.getRelationId())
                    .update();
        } else if (req.getInitCompanyId() == null && !virtualRelated) {
            //A类关联
            Map<Integer, Integer> rawTypeMap = Arrays.stream(LyncsOdsConstant.ContactType.values()).collect(Collectors.toMap(LyncsOdsConstant.ContactType::getKey, LyncsOdsConstant.ContactType::getOpposite));
            for (String t : req.getType().split(LyncsOdsConstant.Common.COMMA)) {
                Integer type = Integer.valueOf(t);
                //正向关联（如果关系已经存在，后面会过滤掉）
                insertList.add(buildContactInfo(companyInfo, relatedCompanyInfo, rawTypeMap.get(type)));
                //反向关联
                insertList.add(buildContactInfo(relatedCompanyInfo, companyInfo, type));
            }
        } else {
            throw new ApiException("好友关系已存在，请勿重复建立关联关系");
        }
        List<CompanyContactInfo> finalInsertList = insertList;
        //数据库中已经存在的好友关系
        List<CompanyContactInfo> existCompanyContactInfos = companyContactInfoService.lambdaQuery().eq(CompanyContactInfo::getStatus, LyncsOdsConstant.BindStatus.BIND.getKey())
                .and(andWrapper -> finalInsertList.forEach(info -> andWrapper.or(orWrapper -> orWrapper.eq(CompanyContactInfo::getCompanyId, info.getCompanyId()).eq(CompanyContactInfo::getRelationId, info.getRelationId()))))
                .list();
        //过滤已存在的好友关系
        if (CollectionUtils.isNotEmpty(existCompanyContactInfos)) {
            List<String> relationKes = existCompanyContactInfos.stream().map(info -> TxnCommonUtils.buildLong2StringKey(info.getCompanyId(), info.getRelationId())).collect(Collectors.toList());
            insertList = insertList.stream().filter(info -> !relationKes.contains(TxnCommonUtils.buildLong2StringKey(info.getCompanyId(), info.getRelationId()))).collect(Collectors.toList());
        }
        if (CollectionUtils.isNotEmpty(insertList)) {
            companyContactInfoService.saveBatch(insertList);
        }
    }

    private CompanyContactInfo buildContactInfo(CompanyInfo companyInfo, CompanyInfo relatedCompanyInfo, Integer type) {
        return new CompanyContactInfo()
                .setId(IdUtil.getSnowflakeNextId())
                .setCompanyId(companyInfo.getId())
                .setRelationId(relatedCompanyInfo.getId())
                .setInitCompanyId(null)
                .setNickname(relatedCompanyInfo.getShortName())
                .setStatus(LyncsOdsConstant.BindStatus.BIND.getKey())
                .setCreateTime(RequestHolder.getRequestDateTime())
                .setType(type);
    }

    @Override
    public String getPartnerType(Long companyId, Long relationId) {
        List<CompanyContactInfo> contactInfos = companyContactInfoService.lambdaQuery()
                .eq(CompanyContactInfo::getCompanyId, relationId)
                .eq(CompanyContactInfo::getRelationId, companyId)
                .eq(CompanyContactInfo::getStatus, LyncsOdsConstant.BindStatus.BIND.getKey())
                .list();
        if (CollectionUtils.isEmpty(contactInfos)) {
            return null;
        }
        return contactInfos.stream().map(CompanyContactInfo::getType).map(String::valueOf).collect(Collectors.joining(LyncsOdsConstant.Common.COMMA));
    }

    @Override
    public void staffJoin(String appId, Long companyId, String code) throws WxErrorException {
        UserInfo userInfo = userService.register(appId, code, null, LyncsOdsConstant.AuthPlatform.WECHAT_WEB_APP.getKey(), Boolean.FALSE);
        if (userInfo == null) {
            throw new ApiException("微信授权失败，请稍后再试");
        }
        Integer count = companyUserRelationService.lambdaQuery()
                .eq(CompanyUserRelation::getCompanyId, companyId)
                .eq(CompanyUserRelation::getUserId, userInfo.getId())
                .eq(CompanyUserRelation::getStatus, LyncsOdsConstant.BindStatus.BIND.getKey())
                .count();
        if (count > 0) {
            throw new ApiException("您已加入该企业，请勿重复操作");
        }
        companyUserRelationService.save(new CompanyUserRelation().setCompanyId(companyId).setUserId(userInfo.getId()).setRole(0).setNickname(userInfo.getUsername()).setCreateTime(RequestHolder.getRequestDateTime()).setUpdateTime(RequestHolder.getRequestDateTime()));
    }

    @Override
    public Map<Integer, Long> partnerCount() {
        List<CompanyContactInfo> contactInfos = companyContactInfoService.lambdaQuery()
                .eq(CompanyContactInfo::getCompanyId, RequestHolder.getCompanyId())
                .eq(CompanyContactInfo::getStatus, LyncsOdsConstant.BindStatus.BIND.getKey())
                .list();
        if (CollectionUtils.isEmpty(contactInfos)) {
            return Map.of(
                    LyncsOdsConstant.ContactType.SUPPLIER.getKey(), 0L,
                    LyncsOdsConstant.ContactType.CUSTOMER.getKey(), 0L
            );
        }
        return contactInfos.stream().collect(Collectors.groupingBy(CompanyContactInfo::getType, Collectors.counting()));
    }

    @Override
    public void saveRoleGroup(SaveCompanyRoleGroupReq req) {
        Long companyId = RequestHolder.getCompanyId();
        Long userId = RequestHolder.getUserId();
        if (CollectionUtils.isNotEmpty(req.getDeleteList())) {
            List<CompanyRelationRole> companyRelationRoles = companyRelationRoleService.lambdaQuery().in(CompanyRelationRole::getGroupId, req.getDeleteList()).list();
            companyRoleGroupService.lambdaUpdate()
                    .set(CompanyRoleGroup::getStatus, LyncsOdsConstant.EnableStatus.DISABLE.getKey())
                    .in(CompanyRoleGroup::getId, req.getDeleteList())
                    .update();
            updateHasRoleGroup(companyRelationRoles, companyId);
        }
        if (CollectionUtils.isNotEmpty(req.getNewList())) {
            List<CompanyRoleGroup> newGroups = req.getNewList().stream().filter(StringUtils::isNotBlank).map(name -> new CompanyRoleGroup().setName(name).setCompanyId(companyId).setCreator(userId)).collect(Collectors.toList());
            companyRoleGroupService.saveBatch(newGroups);
        }
        if (CollectionUtils.isNotEmpty(req.getUpdateList())) {
            List<CompanyRoleGroup> updateGroups = req.getUpdateList().stream().map(r -> new CompanyRoleGroup().setId(r.getId()).setName(r.getName())).collect(Collectors.toList());
            companyRoleGroupService.updateBatchById(updateGroups);
        }
    }

    @Override
    public CommonPage<CompanyRoleGroupResp> listRoleGroup(Integer page, Integer pageSize, Boolean includeCompanyInfo) {
        Long companyId = RequestHolder.getCompanyId();
        LambdaQueryChainWrapper<CompanyRoleGroup> wrapper = companyRoleGroupService.lambdaQuery()
                .eq(CompanyRoleGroup::getCompanyId, companyId)
                .eq(CompanyRoleGroup::getStatus, LyncsOdsConstant.EnableStatus.ENABLE.getKey());
        CommonPage<CompanyRoleGroupResp> commonPage = PageUtil.count(wrapper, page, pageSize);
        if (commonPage.getTotal() == 0) {
            return commonPage;
        }
        List<CompanyRoleGroup> roleGroups = wrapper.orderByAsc(CompanyRoleGroup::getId).last(PageUtil.getPageSqlOnly(page, pageSize)).list();
        Map<Long, Map<Long, String>> roleContactMap = new HashMap<>();
        if (includeCompanyInfo) {
            List<Long> groupIds = roleGroups.stream().map(CompanyRoleGroup::getId).collect(Collectors.toList());
            List<CompanyRelationRole> companyRelationRoles = companyRelationRoleService.lambdaQuery()
                    .eq(CompanyRelationRole::getCompanyId, companyId)
                    .in(CompanyRelationRole::getGroupId, groupIds)
                    .eq(CompanyRelationRole::getStatus, LyncsOdsConstant.EnableStatus.ENABLE.getKey())
                    .list();
            if (CollectionUtils.isNotEmpty(companyRelationRoles)) {
                Map<Long, List<Long>> groupRelationMap = companyRelationRoles.stream().collect(Collectors.groupingBy(CompanyRelationRole::getGroupId, Collectors.mapping(CompanyRelationRole::getRelationId, Collectors.toList())));
                List<Long> relationIds = groupRelationMap.values().stream().flatMap(Collection::stream).distinct().collect(Collectors.toList());
                List<CompanyContactInfo> companyContactInfos = companyContactInfoService.lambdaQuery()
                        .in(CompanyContactInfo::getRelationId, relationIds)
                        .eq(CompanyContactInfo::getCompanyId, companyId)
                        .eq(CompanyContactInfo::getStatus, LyncsOdsConstant.EnableStatus.ENABLE.getKey())
                        .list();
                if (CollectionUtils.isNotEmpty(companyContactInfos)) {
                    Map<Long, CompanyContactInfo> contactInfoMap = companyContactInfos.stream().collect(Collectors.toMap(CompanyContactInfo::getRelationId, o -> o, (o1, o2) -> o1));
                    roleContactMap = groupRelationMap.entrySet().stream().map(entry -> {
                        List<CompanyContactInfo> thisRoleContactInfos = entry.getValue().stream().map(i -> contactInfoMap.getOrDefault(i, null)).filter(Objects::nonNull).collect(Collectors.toList());
                        Map<Long, String> thisRoleContactMap = thisRoleContactInfos.stream().collect(Collectors.toMap(CompanyContactInfo::getRelationId, CompanyContactInfo::getNickname));
                        return Map.of(entry.getKey(), thisRoleContactMap);
                    }).collect(HashMap::new, HashMap::putAll, HashMap::putAll);
                }
            }
        }
        BiFunction<CompanyRoleGroup, Map<Long, Map<Long, String>>, CompanyRoleGroupResp> convertFunc = (info, infoMap) -> {
            Map<Long, String> relationInfoMap = infoMap.getOrDefault(info.getId(), Map.of());
            return new CompanyRoleGroupResp()
                    .setGroupId(info.getId())
                    .setGroupName(info.getName())
                    .setCompanyInfos(relationInfoMap.entrySet().stream().map(entry -> new CompanyRoleGroupResp.RoleGroupCompanyInfo().setRelationId(entry.getKey()).setNickName(entry.getValue())).collect(Collectors.toList()));
        };
        return PageUtil.result(roleGroups, roleContactMap, convertFunc, commonPage);
    }

    @Override
    public void saveStaffGroup(Long companyId, SaveStaffGroupReq req) {
        Map<Long, List<Long>> userGroupIdMap = Map.of(req.getUserId(), req.getGroupIds());
        Set<Long> userIds = userGroupIdMap.keySet();
        companyUserRoleService.lambdaUpdate().eq(CompanyUserRole::getCompanyId, companyId)
                .in(CompanyUserRole::getUserId, userIds)
                .remove();
        List<CompanyUserRole> companyUserRoles = userGroupIdMap.entrySet().stream().flatMap(entry -> {
            if (CollectionUtils.isEmpty(entry.getValue())) {
                return null;
            }
            return entry.getValue().stream().distinct().map(groupId -> new CompanyUserRole().setGroupId(groupId).setUserId(entry.getKey()).setCompanyId(companyId).setStatus(Boolean.TRUE));
        }).filter(Objects::nonNull).collect(Collectors.toList());
        if (CollectionUtils.isEmpty(companyUserRoles)) {
            return;
        }
        companyUserRoleService.saveBatch(companyUserRoles);
    }

    @Override
    public StaffDetailInfoResp getStaffDetail(Long companyId, Long userId) {
        List<CompanyUserRole> list = companyUserRoleService.lambdaQuery()
                .eq(CompanyUserRole::getCompanyId, companyId)
                .eq(CompanyUserRole::getUserId, userId)
                .eq(CompanyUserRole::getStatus, LyncsOdsConstant.EnableStatus.ENABLE.getKey())
                .list();
        List<Long> groupIds = list.stream().map(CompanyUserRole::getGroupId).distinct().collect(Collectors.toList());
        return new StaffDetailInfoResp().setUserId(userId).setGroupIds(groupIds);
    }

    @Override
    public void updateSeal(String seal) {
        this.companyInfoService.lambdaUpdate().set(CompanyInfo::getSeal, seal).eq(CompanyInfo::getId, RequestHolder.getCompanyId()).update();
    }

    @Override
    public String getSeal(Long companyId) {
        CompanyInfo one = this.companyInfoService.getById(companyId);
        return one == null ? null : one.getSeal();
    }

    @Override
    public TransactionInfoResp getTransactionInfo(Long companyId) {
        //this.orderInfoService.lambdaQuery().and()
        return null;
    }

    /**
     * 重新计算改关系是否挂靠权限组
     *
     * @param companyRelationRoles roles
     */
    public void updateHasRoleGroup(List<CompanyRelationRole> companyRelationRoles, Long companyId) {
        if (CollectionUtils.isEmpty(companyRelationRoles) || Objects.isNull(companyId)) {
            return;
        }
        List<Long> relationIds = companyRelationRoles.stream().map(CompanyRelationRole::getRelationId).distinct().collect(Collectors.toList());
        List<CompanyRelationRoleCount> validRoleCountList = companyRelationRoleService.getValidRoleCount(companyId, relationIds);
        List<Long> noRoleGroupRelationIds = validRoleCountList.stream().filter(cnt -> cnt.getCnt() == 0).map(CompanyRelationRoleCount::getRelationId).collect(Collectors.toList());
        if (CollectionUtils.isEmpty(noRoleGroupRelationIds)) {
            return;
        }
        companyContactInfoService.lambdaUpdate()
                .set(CompanyContactInfo::getHasRoleGroup, Boolean.FALSE)
                .in(CompanyContactInfo::getRelationId, noRoleGroupRelationIds)
                .eq(CompanyContactInfo::getCompanyId, companyId)
                .update();
    }

    @Deprecated
    private List<CompanyContactInfo> getAllContactInfos(SavePartnerCompanyReq req, List<CompanyContactInfo> exitContactInfos, CompanyInfo virtualCompany) {
        //支持的所有关系类型
        List<Integer> rawType = Arrays.stream(LyncsOdsConstant.ContactType.values()).map(LyncsOdsConstant.ContactType::getKey).collect(Collectors.toList());
        List<CompanyContactInfo> contactInfos = new ArrayList<>();
        //已存在的好友关系
        Map<Integer, CompanyContactInfo> relationTypeMap = exitContactInfos.stream().collect(Collectors.toMap(CompanyContactInfo::getType, o -> o, (o1, o2) -> o2));

        List<Integer> relationType = Arrays.stream(req.getType().split(LyncsOdsConstant.Common.COMMA)).map(Integer::valueOf).distinct().collect(Collectors.toList());
        relationType.forEach(type -> {
            if (!rawType.contains(type)) {
                throw new ApiException("invalid relation type:" + type);
            }
            CompanyContactInfo companyContactInfo = relationTypeMap.get(type);
            if (Objects.isNull(companyContactInfo)) {
                companyContactInfo = new CompanyContactInfo()
                        .setCompanyId(RequestHolder.getCompanyId())
                        .setInitCompanyId(virtualCompany.getId())
                        .setRelationId(virtualCompany.getId())
                        .setStatus(LyncsOdsConstant.BindStatus.BIND.getKey())
                        .setCreateTime(RequestHolder.getRequestDateTime())
                        .setType(type);
            }
            companyContactInfo.setNickname(req.getNickname());
            contactInfos.add(companyContactInfo);
        });
        return contactInfos;
    }

    private List<CompanyFeeInfo> getCommonFeeInfos() {
        return companyFeeInfoService.lambdaQuery().eq(CompanyFeeInfo::getCompanyId, 0).eq(CompanyFeeInfo::getStatus, 1).list();
    }

    private void companyUserRelation(Long userId, Long cid, Integer role) {
        UserInfo userInfo = userInfoService.lambdaQuery().eq(UserInfo::getId, userId).one();
        companyUserRelationService.save(new CompanyUserRelation().setCompanyId(cid).setUserId(userId).setRole(role).setNickname(userInfo.getUsername()).setCreateTime(RequestHolder.getRequestDateTime()).setUpdateTime(RequestHolder.getRequestDateTime()));
        relationRecordService.save(new CompanyUserRelationLog().setId(IdUtil.getSnowflakeNextId()).setUserId(userId).setCompanyId(cid).setStatus(LyncsOdsConstant.BindStatus.BIND.getKey()));
    }
}
